package org.jeecg.generate;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

/**
 * xml转为实体类（包括赋值）
 *
 * @author liujiamiao
 * @email 2544296058@qq.com
 * @date 2020-10-26 21:23
 */
public class XmlGenerate {

    //包名
    private static String packageName = null;
    //文件读取路径以及生成路径
    private static String path = null;
    //记录已生成的类名
    private static Set<String> clazzList = new HashSet<String>();

    //@XmlAttribute("type-ggg")

    public static void main(String[] args) throws IOException {
        //文件路径
        String jsonName = "GenerateXml.xml";
        //包名
        packageName = "com.chunuo.xml";
        //路径
        path = "D:\\JAVA\\poi\\";

        //读取文件内容
        String xml = read(path, jsonName);

        init(xml);

    }

    public static void init(String xml) throws IOException {

        //初始化xml文本，找到类标签（最外层的标签）
        int index = xml.indexOf("?>")+2;
        if(index==-1){
            throw new RuntimeException("没有找到xml的头信息，不是一个正确的xml！");
        }
        //类名
        String clazzName = null;

        //封装了查找标签的下标
        TagIndexClazz tagIndexClazz = new TagIndexClazz();

        //标签体内容  如： <xxx>aaa</xxx>中的aaa
        String tempXml = null;

        //遍历找到类标签
        for(int i = index;i<xml.length();i++){
            if(xml.charAt(i)=='<'){

                tagIndexClazz.i = i;
                //获取标签名
                String tagName = getBeginTagIndex(xml, tagIndexClazz,false);

                //找结束标签，并判断是否格式正确
                tempXml = getEndTagIndex(xml, tagIndexClazz, tagName, true);

                //一切都找完了后，说明已经找到了类名
                clazzName = tagName;
                break;
            }
        }

        build(tempXml,tagIndexClazz,clazzName);

    }

    /**
     * 遍历主类下的所有标签
     * @param tempXml
     * @param tagIndexClazz
     * @param clazzTagName
     */
    private static void build(String tempXml,TagIndexClazz tagIndexClazz, String clazzTagName) throws IOException {
        boolean isList = false;
        //list集合的话，类名是小写的
        if(clazzTagName.charAt(0)>=97&&clazzTagName.charAt(0)<=122){
            isList = true;
        }
        //不是集合的话，表示该类已创建
        if(!isList){
            clazzList.add(clazzTagName);
        }
        //遍历属性
        for(int i = 0;i<tempXml.length();i++){
            if(tempXml.charAt(i)=='<'){
                tagIndexClazz.i = i;
                //标签名
                String temp = getBeginTagIndex(tempXml, tagIndexClazz,isList);
                //不等于null，说明不是<xxx/>标签，需要找结束标签
                if(temp!=null){
                    getEndTagIndex(tempXml, tagIndexClazz, temp,false);
                    if(tagIndexClazz.left< tagIndexClazz.right){
                        String content = tempXml.substring(tagIndexClazz.left, tagIndexClazz.right).trim();
                        if(!content.isEmpty()&&content.indexOf("<")==-1){
                            //只是属性
                            tagIndexClazz.fields.put(temp,content);
                        }else if(!content.isEmpty()&&content.indexOf("<")!=-1){
                            //可能是list或者是 类
                            if(temp.indexOf("List")!=-1||temp.charAt(temp.length()-1)=='s'){
                                //集合
                                int tempIndex = temp.indexOf("List");
                                if(tempIndex==-1){
                                    tempIndex = temp.length()-1;
                                }
                                //标签类名变大写
                                String clazzType = temp.substring(0, tempIndex);
                                //本类上添加list集合
                                tagIndexClazz.fields.put(clazzType,"List<"+clazzType+">:Object");
                                //给这组list对象生成类
                                TagIndexClazz tempTagIndexClazz = new TagIndexClazz();
                                if(!isList){
                                    build(content,tempTagIndexClazz,clazzType);
                                }else{
                                    //是集合的话先把字段封装好
                                    throw new RuntimeException("集合嵌套集合异常！");
                                }
                            }else{
                                //类
                                //标签类名变大写
                                temp = temp.substring(0, 1).toUpperCase() + temp.substring(1, temp.length());
                                TagIndexClazz tempTagIndexClazz = new TagIndexClazz();
                                if(!isList){
                                    tagIndexClazz.fields.put(temp,temp+":Object");
                                }else{
                                    //是集合的话把原来的属性集合复制过来，因为属性集合需要导入所有集合中的属性，才能确保没有忽略的字段
                                    tempTagIndexClazz.fields = tagIndexClazz.fields;
                                }
                                build(content,tempTagIndexClazz,temp);
                            }
                        }
                    }
                }
                //tagIndexClazz.end  ==》 <xxxx/>或<xxx></xxx>中  > 的下标
                i = tagIndexClazz.end;
            }
        }
        if(!isList)
            buildClazz(tagIndexClazz, clazzTagName);
    }

    /**
     * 写入一个java类
     * @param tagIndexClazz
     * @param clazzTagName
     */
    private static void buildClazz(TagIndexClazz tagIndexClazz, String clazzTagName) throws IOException {
        StringBuilder java = new StringBuilder(
                "package "+packageName+";\r\n\r\n" +
                "import java.util.*;\r\n\r\n" +
                "/**\r\n" +
                " * @author liujiamiao\r\n" +
                " * @email 2544296058@qq.com\r\n" +
                " */\r\n" +
                "public class "+clazzTagName+" {\r\n");
        Set<Map.Entry<String, Object>> entrySet = tagIndexClazz.fields.entrySet();
        for(Map.Entry entry:entrySet){
            java.append("\tprivate "+getType(entry.getValue())+" "+entry.getKey()+";\r\n");
        }

        //构造方法
        java.append("\tpublic "+clazzTagName+"() {\r\n");

        //构造方法的初始化代码
//            for(String sb : list){
//                java.append(sb);
//            }

        //结束
        java.append("\t}\r\n}");

        System.out.println(java.toString());
        //写入到java类中
        write(path+(packageName.replace('.','\\')),clazzTagName+".java",java.toString());


    }


    /**
     * 找到开始的标签，并返回标签名
     * 如果即不是<xxx/>属性，又不是<xxxx xxx="xx"/>类，那么放回的就是<xxx>中的xxx标签名==》属性名
     * @param xml
     * @param tagIndexClazz
     * @return
     */
    public static String getBeginTagIndex(String xml,TagIndexClazz tagIndexClazz,boolean isList) throws IOException {

        //遍历标签过程中，可能遇到类标签，这时候属性就不能添加到类标签里了，而是添加到这个里面，最后再写入到新的类文件里
        TagIndexClazz tempTagIndexClazz = null;
        if(!isList){
           tempTagIndexClazz = new TagIndexClazz();
        }else{
            tempTagIndexClazz = tagIndexClazz;
        }

        tagIndexClazz.j = tagIndexClazz.i+1;
        //第一个<后，开始找结束点，有" ","/",">"  三种可能
        while(!(xml.charAt(tagIndexClazz.j)==' '||xml.charAt(tagIndexClazz.j)=='/'||xml.charAt(tagIndexClazz.j)=='>')){
            if(xml.charAt(tagIndexClazz.j)=='<'){
                throw new RuntimeException("一个标签出现两个\"<<\"！");
            }
            tagIndexClazz.j++;
        }
        //tagIndexClazz.j = 标签名的结束符 如 <abc>   =  >  c+1的下标

        //找标签的 ">" 下标
        tagIndexClazz.left = tagIndexClazz.j;

        while(xml.charAt(tagIndexClazz.left)!='>'){
            if(xml.charAt(tagIndexClazz.left)=='<'){
                throw new RuntimeException("一个标签出现两个\"<<\"！");
            }

            //判断是否含有  />
            if(xml.charAt(tagIndexClazz.left)=='/'){
                if(xml.charAt(tagIndexClazz.left+1)=='>'){
                    if(tempTagIndexClazz.fields.size()==0){
                        //属性标签
                        tagIndexClazz.fieldName = xml.substring(tagIndexClazz.i+1, tagIndexClazz.left).trim();
                        tagIndexClazz.fields.put(tagIndexClazz.fieldName,"Object:Object");
                    }else{
                        //可以结束，如何处理？？
                        //没有子类，可以直接返回空值，并写入一个java类
                        tagIndexClazz.fieldName = xml.substring(tagIndexClazz.i+1, tagIndexClazz.j).trim();
                        String clazzName = tagIndexClazz.fieldName.substring(0,1).toUpperCase()+ tagIndexClazz.fieldName.substring(1);
                        if(!isList){
                            tagIndexClazz.fields.put(tagIndexClazz.fieldName,clazzName+":Object");
                        }
                        buildClazz(tempTagIndexClazz, clazzName);

                    }
                    tagIndexClazz.end = tagIndexClazz.left+1;
                    return null;
                }else{
                    throw new RuntimeException("语法不规范！ ："+xml.substring(tagIndexClazz.i, tagIndexClazz.left+1));
                }
            }
            //即不是>，又不是空格，说明有属性
            else if(xml.charAt(tagIndexClazz.left)!=' '&&xml.charAt(tagIndexClazz.left)!='"') {
                //返回该值是因为容易读懂代码
                tempTagIndexClazz.left = tagIndexClazz.left;
                tagIndexClazz.left = getTagField(xml,tempTagIndexClazz);
            }
            tagIndexClazz.left++;


        }

        //<home type-ggg="AASX" menuchoice="56768"></home>类标签可能是这种格式
        if(xml.charAt(tagIndexClazz.left)=='>'){
            if(tempTagIndexClazz.fields.size()>0){
                tagIndexClazz.fieldName = xml.substring(tagIndexClazz.i+1, tagIndexClazz.j).trim();
                String clazzName = tagIndexClazz.fieldName.substring(0,1).toUpperCase()+ tagIndexClazz.fieldName.substring(1);
                if(!isList){
                    tagIndexClazz.fields.put(tagIndexClazz.fieldName,clazzName+":Object");
                }
                buildClazz(tempTagIndexClazz, clazzName);
            }
        }

        tagIndexClazz.left+=1;

        //找到后截取
        String tagName = xml.substring(tagIndexClazz.i+1, tagIndexClazz.j);
        return tagName;

    }

    /**
     * 找到标签里面的属性名和属性值
     * @param xml
     * @param tagIndexClazz
     * @return
     */
    public static int getTagField(String xml,TagIndexClazz tagIndexClazz){
        //说明可以获取属性名
        int temp = tagIndexClazz.left+1;
        //属性名和属性值的拆分点必须有"="
        while(xml.charAt(temp)!='=') {
            if(xml.charAt(tagIndexClazz.left)=='<'){
                throw new RuntimeException("一个标签出现两个\"<<\"！");
            }
            //还没有找到等于号，先找到引号，语法不规范
            if(xml.charAt(temp)=='"') {
                throw new RuntimeException("语法不规范！ ："+xml.substring(tagIndexClazz.left,temp+1));
            }
            temp++;
        }
        //找到属性名
        String field = xml.substring(tagIndexClazz.left, temp).trim();
        String[] split = field.split("-");
        if(split.length>1){
            StringBuilder sbField = new StringBuilder();
            for(int i = 0;i<split.length;i++){
                if(i>0){
                    sbField.append(split[i].substring(0,1).toUpperCase()+split[i].substring(1));
                }else{
                    sbField.append(split[i]);
                }
            }
            field = sbField.toString();
        }



        //开始找field属性的值
        //属性值必定有"""
        while(xml.charAt(temp)!='"') {
            if(xml.charAt(tagIndexClazz.left)=='<'){
                throw new RuntimeException("一个标签出现两个\"<<\"！");
            }
            temp++;
        }
        temp++;
        //再找下一个”引号
        int fieldValueLeft = temp;
        while(xml.charAt(temp)!='"') {
            if(xml.charAt(tagIndexClazz.left)=='<'){
                throw new RuntimeException("一个标签出现两个\"<<\"！");
            }
            temp++;
        }
        //将属性值和属性名存进去
        tagIndexClazz.fields.put(field,xml.substring(fieldValueLeft,temp));
        //返回目前找到的下标
        return temp;
    }

    /**
     * 找结束标签，并判断是否格式正确
     * @param xml
     * @param tagIndexClazz
     * @param tagName
     * @param flag 是否是刚开始的类标签，是的话从最后开始查找结束标签，否则从前面开始查找结束标签
     */
    public static String getEndTagIndex(String xml,TagIndexClazz tagIndexClazz,String tagName,boolean flag){
        //开始找结束标签
        int lastIndex = -1;
        if(flag){
            lastIndex = xml.substring(tagIndexClazz.left).lastIndexOf(tagName);
        }else{
            lastIndex = xml.substring(tagIndexClazz.left).indexOf(tagName);
        }

        //如果没有找到则抛异常
        if(lastIndex==-1){
            throw new RuntimeException("没有找到<"+tagName+">的结束标签！");
        }

        //lastIndex + tagIndexClazz.left 得到的是</xxxx>中xxxx的下标，加上xxxx的长度，得到的下标就是  >
        tagIndexClazz.end = lastIndex + tagIndexClazz.left + tagName.length();
        //找到 />的下标是 >下标
        while (xml.charAt(tagIndexClazz.end)!='>'){
            if(xml.charAt(tagIndexClazz.end)!=' '){
                throw new RuntimeException("请检查<"+tagName+">标签是否正确！");
            }
            tagIndexClazz.end++;
        }

        //找到后记得加上left，因为上面截短了left个字符
        lastIndex+= tagIndexClazz.left;
        //判断找到的结束标签是否正确，是否有"/"结尾
        while(xml.charAt(lastIndex-1)!='/'){
            if(xml.charAt(lastIndex-1)!=' '){
                throw new RuntimeException("请检查<"+tagName+">标签是否正确！");
            }
            lastIndex--;
        }

        //找到类标签的结束标签下标 </xxxx>  的<下标
        tagIndexClazz.right = lastIndex-1;
        while(xml.charAt(tagIndexClazz.right)!='<'){
            tagIndexClazz.right--;
        }
        return xml.substring(tagIndexClazz.left , tagIndexClazz.right);
    }

    /**
     * 根据value获取属性类型
     * @param value
     * @return
     */
    private static String getType(Object value) {
        int len = 0;
        if(value.toString().indexOf("Object")!=-1){
            String[] key_value = value.toString().split(":");
            if(key_value.length<2){
                return "String";
            }
            return key_value[0];
        } else if((len = value.toString().indexOf('.'))!=-1){
            //精度小于6的是float类型，否则是double类型
            if(value.toString().length()-len<=6){
                try {
                    Float.parseFloat(value.toString());
                    return "Float";
                } catch (NumberFormatException e) {
                    return "String";
                }
            }else{
                try {
                    Double.parseDouble(value.toString());
                    return "Double";
                } catch (NumberFormatException e) {
                    return "String";
                }
            }
        }else if(value.toString().equals("true")||value.toString().equals("false")){
            return "Boolean";
        }else {
            try {
                Integer.parseInt(value.toString());
                return "Integer";
            } catch (NumberFormatException e) {
                return "String";
            }
        }
    }


    /**
     * 读取文件内容
     * @param path
     * @param fileName
     * @return
     * @throws IOException
     */
    public static String read(String path ,String fileName) throws IOException {
        //读取
        FileReader reader = new FileReader(new File(path,fileName));
        char[] buf = new char[50];
        int len = -1;
        StringBuilder sb = new StringBuilder();
        while((len = reader.read(buf))!=-1){
            sb.append(new String(buf,0,len));
        }
        reader.close();
        return sb.toString();
    }

    /**
     * 写入文件
     * @param path
     * @param fileName
     * @param content
     * @throws IOException
     */
    public static void write(String path,String fileName,String content) throws IOException {
        File filePath = new File(path);
        if(!filePath.exists()){
            filePath.mkdirs();
        }
        File file = new File(path, fileName);
        FileWriter writer = new FileWriter(file);
        writer.write(content);
        writer.flush();
        writer.close();
    }

    //去掉多余的空格
    public static String trim(String str){
        char[] ch = str.toCharArray();
        StringBuilder sb = new StringBuilder();
        //前一个字符是否是空格
        boolean flag = false;
        for(int i = 0;i<ch.length;i++){
            if(ch[i]!=' '){
                sb.append(ch[i]);
                flag = false;
            }else{
                //前一个不是空格
                if(!flag){
                    flag = true;
                    sb.append(ch[i]);
                }
            }
        }
        return sb.toString();
    }

    public static void show(List list){
        for(int i = 0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }

}

@NoArgsConstructor
@AllArgsConstructor
@Data
class TagIndexClazz{
    //<下标
    Integer i;

    //标签名最后一位的下标
    Integer j;

    //>下标
    Integer left;

    //</xxxx>的下标
    Integer right;

    //结束标签 />
    Integer end;

    //属性名
    String fieldName;

    //记录所有的属性
    Map<String,Object> fields = new HashMap<>();
}
